package com.amumu.drama.common.modules.service.impl.payment.handler;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.amumu.drama.common.constant.FuiouPayTypeEnum;
import com.amumu.drama.common.constant.OperateTypeEnum;
import com.amumu.drama.common.constant.PayStatusEnum;
import com.amumu.drama.common.modules.fuioupay.config.FuiouConfig;
import com.amumu.drama.common.modules.fuioupay.constant.FuiouOrderStatusEnum;
import com.amumu.drama.common.modules.fuioupay.utils.FuiouHttpUtil;
import com.amumu.drama.common.modules.fuioupay.utils.RsaUtil;
import com.amumu.drama.common.modules.order.dto.OmsOrderDetail;
import com.amumu.drama.common.modules.order.manager.OrderManager;
import com.amumu.drama.common.modules.service.impl.payment.FuiouPcPaymentResp;
import com.amumu.drama.common.modules.service.impl.payment.PaymentReq;
import com.amumu.drama.common.modules.service.impl.payment.PaymentResp;
import com.amumu.drama.model.OmsOrder;
import com.amumu.drama.model.OmsOrderItem;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class FuiouPcPaymentHandler implements PaymentHandler {

    @Autowired
    private FuiouConfig fuiouConfig;

    @Autowired
    private OrderManager orderManager;


    @Override
    public String[] type() {
        return new String[]{
//                PaymentFactory.getPayHandlerType(PayTypeEnum.WX_PAY, PayProviderEnum.FUIOU_PAY, SourceTypeEnum.PC),
//                PaymentFactory.getPayHandlerType(PayTypeEnum.ALI_PAY, PayProviderEnum.FUIOU_PAY, SourceTypeEnum.PC),
        };
    }

    @Override
    public FuiouPcPaymentResp pay(PaymentReq param) {
        OmsOrderDetail orderDetail = param.getOrderDetail();
        List<OmsOrderItem> orderItemList = orderDetail.getOrderItemList();
        OmsOrderItem omsOrderItem = orderItemList.get(0);
        log.info("开始富友支付：订单ID：{},支付类型：{}", orderDetail.getOrderSn(), orderDetail.getPayType());
        Map<String, String> payReqParams = buildPayReqParams(param, orderDetail, omsOrderItem);
        FuiouPcPaymentResp paymentResp = new FuiouPcPaymentResp();
        paymentResp.setApplyTime(LocalDateTime.now());
        try {
            //富友公钥加密
            String payReqStr = JSON.toJSONString(payReqParams);
            paymentResp.setReqInfo(payReqStr);
            String message = RsaUtil.encryptByRsaPub(payReqStr, fuiouConfig.getPubKey(), "GBK");
            paymentResp.setSign(message);
            paymentResp.setPayUrl(fuiouConfig.getPcUrl());
            paymentResp.setPayParams(payReqParams);
            paymentResp.setPayType(FuiouPayTypeEnum.getPayTypeByTypeEnum(param.getPayTypeEnum(), param.getSourceTypeEnum()));
            paymentResp.setPayStatusEnum(PayStatusEnum.PAYING);
            paymentResp.setRespMsg("success");

        } catch (Exception e) {
            //更新订单状态为支付失败
            paymentResp.setPayStatusEnum(PayStatusEnum.FAIL);
            paymentResp.setPayRemark(e.getMessage());
            log.error("富友支付支付异常，orderId:{},orderSn:{},orderType:{}", orderDetail.getId(), orderDetail.getOrderSn(), orderDetail.getPayType(), e);
        }
        return paymentResp;
    }

    private Map<String, String> buildPayReqParams(PaymentReq param, OmsOrderDetail orderDetail, OmsOrderItem omsOrderItem) {
        Map<String, String> payReqParam = new HashMap();
        payReqParam.put("mchnt_cd", fuiouConfig.getMchntCd());
        payReqParam.put("order_date", new SimpleDateFormat("yyyyMMdd").format(new Date()));
        payReqParam.put("order_id", orderDetail.getOrderSn());

        //支付金额
        payReqParam.put("order_amt", orderDetail.getPayAmount().multiply(new BigDecimal("100")).intValue() + "");
        payReqParam.put("page_notify_url", "");
        payReqParam.put("back_notify_url", param.getNotifyUrl());

        payReqParam.put("goods_name", param.getSubject());
        payReqParam.put("pay_type", FuiouPayTypeEnum.getPayTypeByTypeEnum(param.getPayTypeEnum(), param.getSourceTypeEnum())); //测试环境用支付宝1分钱测试。 其他支付方式生产测试。
        return payReqParam;
    }


    private Map<String, String> buildRefundReqParams(PaymentReq param, OmsOrder omsOrder) {
        Map<String, String> payReqParam = new HashMap();
        payReqParam.put("mchnt_cd", fuiouConfig.getMchntCd());
        payReqParam.put("refund_order_date", new SimpleDateFormat("yyyyMMdd").format(new Date()));
        payReqParam.put("refund_order_id", omsOrder.getOrderSn());
        payReqParam.put("pay_order_date", LocalDateTimeUtil.format(omsOrder.getPaymentTime(), "yyyyMMdd"));
        payReqParam.put("pay_order_id", omsOrder.getOrderSn());
        //退款金额
        param.setPayAmount(omsOrder.getPayAmount());
        payReqParam.put("refund_amt", omsOrder.getPayAmount().multiply(new BigDecimal("100")).intValue() + "");
        payReqParam.put("back_notify_url", param.getRefundNotifyUrl());
        payReqParam.put("ver", "1.0.0");
        return payReqParam;
    }

    /**
     * 查询订单的支付状态
     *
     * @param orderDetail
     */
    @Override
    public PaymentResp queryPayStatus(OmsOrderDetail orderDetail) {
        Map<String, String> payReqParams = Maps.newHashMap();
        payReqParams.put("mchnt_cd", fuiouConfig.getMchntCd());
        payReqParams.put("order_date", DateUtil.format(orderDetail.getPaymentTime(), "yyyyMMdd"));
        payReqParams.put("order_id", orderDetail.getPayOrderSn());
        payReqParams.put("ver", "4.0.0");
        String payReqStr = JSON.toJSONString(payReqParams);
        String message = RsaUtil.encryptByRsaPub(payReqStr, fuiouConfig.getPubKey(), "GBK");
        Map<String, String> req = new HashMap<String, String>();
        req.put("mchnt_cd", fuiouConfig.getMchntCd());
        req.put("message", message);
        String resp = FuiouHttpUtil.postJson(fuiouConfig.getPayQueryUrl(), JSON.toJSONString(req));
        PaymentResp paymentResp = payNotify(null, resp);
        paymentResp.setReqInfo(payReqStr);
        return paymentResp;
    }

    /**
     * 支付回调通知
     *
     * @param params
     */
    @Override
    public PaymentResp payNotify(Map<String, String> params, String body) {
        PaymentResp paymentResp = new PaymentResp();
        paymentResp.setPayRemark("success");
        try {
            paymentResp.setRespInfo(body);
            JSONObject json = JSON.parseObject(body);
            String resp_code = json.getString("resp_code");
            String resp_desc = json.getString("resp_desc");
            paymentResp.setRespMsg(resp_desc);
            if (resp_code.equals("0000")) {
                //商户私钥解密
                String msg = RsaUtil.decryptByRsaPri(json.getString("message"), fuiouConfig.getPriKey(), "GBK");
                json.put("message", msg);
                String jsonString = json.toJSONString();
                log.info("订单结果回调或者查询：富友回调结果:{}", jsonString);
                paymentResp.setRespInfo(jsonString);
                cn.hutool.json.JSONObject payMessage = JSONUtil.parseObj(msg);
                String payOrderSn = payMessage.getStr("order_id");
                paymentResp.setPayOrderSn(payOrderSn);
                Integer orderSt = payMessage.getInt("order_st");
                fillPaymentRespPayStatus(orderSt, paymentResp);
            } else {
                log.error("富友支付接口回调数据异常 msg： {} ", json);
                paymentResp.setPayStatusEnum(PayStatusEnum.LOG);
            }
        } catch (Exception e) {
            log.error("富友接口回调数据异常 error：", e);
            paymentResp.setPayStatusEnum(PayStatusEnum.LOG);
            paymentResp.setPayRemark(e.getMessage());
        }
        return paymentResp;
    }

    private static void fillPaymentRespPayStatus(Integer orderSt, PaymentResp paymentResp) {
        String desc = FuiouOrderStatusEnum.getDescByCode(orderSt);
        paymentResp.setPayRemark(desc);
        //订单未支付
        switch (orderSt) {
            //订单未支付
            case 0:
                paymentResp.setPayStatusEnum(PayStatusEnum.PAYING);
                break;
            //支付成功
            case 1:
                paymentResp.setPayStatusEnum(PayStatusEnum.SUCCESS);
                paymentResp.setApplyConfirmTime(LocalDateTime.now());
                break;
            case 5:
                paymentResp.setPayStatusEnum(PayStatusEnum.REFUND);
                paymentResp.setApplyConfirmTime(LocalDateTime.now());
                break;
            case 6:
                paymentResp.setPayStatusEnum(PayStatusEnum.REFUND);
                paymentResp.setApplyConfirmTime(LocalDateTime.now());
                break;
            case 7:
                paymentResp.setPayStatusEnum(PayStatusEnum.REFUND_FAIL);
                paymentResp.setApplyConfirmTime(LocalDateTime.now());
                break;
            case 8:
                paymentResp.setPayStatusEnum(PayStatusEnum.REFUNDING);
                break;
            case 9:
                paymentResp.setPayStatusEnum(PayStatusEnum.REFUNDING);
                break;
            default:
                paymentResp.setPayStatusEnum(PayStatusEnum.FAIL);
                paymentResp.setApplyConfirmTime(LocalDateTime.now());
                break;
        }
    }

    @Override
    public PaymentResp refund(PaymentReq param, OperateTypeEnum operateTypeEnum, Long operatorId) {
        OmsOrder omsOrder = orderManager.getOmsOrderHighly(param.getOmsOrder(), param.getOrderId(), null, null);
        log.info("开始富友发起退款申请：订单ID：{},支付类型：{}", omsOrder.getId(), omsOrder.getPayType());
        PaymentResp paymentResp = new PaymentResp();
        paymentResp.setApplyTime(LocalDateTime.now());
        try {
            //富友公钥加密
            Map<String, String> payReqParams = buildRefundReqParams(param, omsOrder);
            paymentResp.setPayAmount(param.getPayAmount());
            String payReqStr = JSON.toJSONString(payReqParams);
            paymentResp.setReqInfo(payReqStr);
            String message = RsaUtil.encryptByRsaPub(payReqStr, fuiouConfig.getPubKey(), "GBK");
            Map<String, String> req = new HashMap<String, String>();
            req.put("mchnt_cd", fuiouConfig.getMchntCd());
            req.put("message", message);
            String resp = FuiouHttpUtil.postJson(fuiouConfig.getRefundUrl(), JSON.toJSONString(req));
            paymentResp.setRespInfo(resp);
            JSONObject json = JSON.parseObject(resp);
            String respCode = json.getString("resp_code");
            String respDesc = json.getString("resp_desc");
            paymentResp.setRespMsg(respDesc);
            if (respCode.equals("0000")) {
                //商户私钥解密
                String msg = RsaUtil.decryptByRsaPri(json.getString("message"), fuiouConfig.getPriKey(), "GBK");
                json.put("message", msg);
                paymentResp.setRespInfo(json.toJSONString());
                paymentResp.setPayStatusEnum(PayStatusEnum.REFUNDING);
                cn.hutool.json.JSONObject payJson = JSONUtil.parseObj(msg);
                paymentResp.setPayUrl(payJson.getStr("token_url"));
                paymentResp.setPayOrderSn(payJson.getStr("order_id"));
                paymentResp.setRespMsg("success");
            } else {
                paymentResp.setPayStatusEnum(PayStatusEnum.LOG);
                log.error("富友支付发起退款失败:{}，orderId:{},orderSn:{},orderType:{}", respDesc, omsOrder.getId(), omsOrder.getOrderSn(), omsOrder.getPayType());
            }
        } catch (Exception e) {
            //更新订单状态为支付失败
            paymentResp.setPayStatusEnum(PayStatusEnum.LOG);
            paymentResp.setPayRemark(e.getMessage());
            log.error("富友支付发起退款异常，orderId:{},orderSn:{},orderType:{}", omsOrder.getId(), omsOrder.getOrderSn(), omsOrder.getPayType(), e);
        }
        return paymentResp;
    }

    /**
     * 退款回调通知
     *
     * @param params
     * @param body
     */
    @Override
    public PaymentResp refundNotify(Map<String, String> params, String body) {
        PaymentResp paymentResp = new PaymentResp();
        paymentResp.setRespMsg("success");
        try {
            paymentResp.setRespInfo(body);
            JSONObject json = JSON.parseObject(body);
            String respCode = json.getString("resp_code");
            String respDesc = json.getString("resp_desc");
            paymentResp.setRespMsg(respDesc);
            if (respCode.equals("0000")) {
                //商户私钥解密
                String msg = RsaUtil.decryptByRsaPri(json.getString("message"), fuiouConfig.getPriKey(), "GBK");
                json.put("message", msg);
                String jsonString = json.toJSONString();
                log.info("富友支付退款回调或者查询：富友回调结果:{}", jsonString);
                paymentResp.setRespInfo(jsonString);
                cn.hutool.json.JSONObject payMessage = JSONUtil.parseObj(msg);
                String payOrderSn = payMessage.getStr("refund_order_id");
                paymentResp.setPayOrderSn(payOrderSn);
                Integer orderSt = payMessage.getInt("refund_st");
                fillPaymentRespPayStatus(orderSt, paymentResp);
                String refundAmt = payMessage.getStr("refund_amt");
                paymentResp.setPayAmount(new BigDecimal(refundAmt).divide(new BigDecimal("100")));
            } else {
                log.error("富友退款回调数据异常 msg： {} ", json);
                paymentResp.setPayStatusEnum(PayStatusEnum.LOG);
            }
        } catch (Exception e) {
            log.error("富友退款回调数据异常，paymentResp:[{}] error：", JSONUtil.toJsonStr(paymentResp), e);
            paymentResp.setPayStatusEnum(PayStatusEnum.LOG);
            paymentResp.setPayRemark(e.getMessage());
        }
        return paymentResp;
    }

    /**
     * 查询订单的退款状态
     *
     * @param omsOrder
     */
    @Override
    public PaymentResp queryRefundStatus(OmsOrder omsOrder) {
        Map<String, String> payReqParams = Maps.newHashMap();
        payReqParams.put("mchnt_cd", fuiouConfig.getMchntCd());
        payReqParams.put("refund_order_date", DateUtil.format(omsOrder.getPaymentTime(), "yyyyMMdd"));
        payReqParams.put("refund_order_id", omsOrder.getPayOrderSn());
        payReqParams.put("ver", "4.0.0");
        String payReqStr = JSON.toJSONString(payReqParams);
        String message = RsaUtil.encryptByRsaPub(payReqStr, fuiouConfig.getPubKey(), "GBK");
        Map<String, String> req = new HashMap<String, String>();
        req.put("mchnt_cd", fuiouConfig.getMchntCd());
        req.put("message", message);
        String resp = FuiouHttpUtil.postJson(fuiouConfig.getRefundQueryUrl(), JSON.toJSONString(req));
        PaymentResp paymentResp = refundNotify(null, resp);
        paymentResp.setReqInfo(payReqStr);
        return paymentResp;
    }
}
